package com.linwoain.oss.plugin

import com.aliyun.oss.OSSClient
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.cxx.json.jsonStringOf
import com.google.gson.Gson
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.MultiFormatWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import org.apache.tools.ant.filters.StringInputStream
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.awt.Color
import java.awt.image.BufferedImage
import java.io.File
import java.net.URL
import java.util.*
import javax.imageio.ImageIO

class OssUploadPlugin : Plugin<Project> {
    companion object {
        private const val DEBUG = true
    }

    private val isMac by lazy {
        System.getProperty("os.name").toLowerCase() == "mac os x"
    }
    private val isLinux by lazy {
        System.getProperty("os.name").toLowerCase() == "linux"
    }
    private val String.upFirst: String
        get() {
            return if (this.isEmpty()) {
                this
            } else {
                this[0].toUpperCase() + this.substring(1)
            }
        }
    private val String.upFirstDeleteLine: String
        get() {
            return if (this.isEmpty()) {
                this
            } else {
                buildString {
                    this@upFirstDeleteLine.split("-").forEachIndexed { index, s ->
                        if (index == 0) {
                            append(s)
                        } else {
                            append(s.upFirst)
                        }

                    }
                }
            }
        }


    override fun apply(project: Project) {
        log(System.getProperty("os.name"))
        val devVersion = Runtime.getRuntime()
            .exec("git rev-list HEAD --first-parent --count").inputStream.bufferedReader()
            .readText().trim().toInt()
        val info =
            Runtime.getRuntime().exec("git log --oneline\n").inputStream.bufferedReader().readText()
        println("info = $info")
        val android = project.extensions.getByType(BaseExtension::class.java)
        project.extensions.create("oss", OssConfig::class.java)
        android.defaultConfig.buildConfigField("int", "DEV_VERSION", devVersion.toString())
        android.variantFilter { variant ->
            val variantName = variant.name
            val name = variantName.upFirst
            variant.flavors.forEach {
                log(it.name)
            }
            val task = project.tasks.create("upload${name}ApkOss")
            task.group = "run"
            val elements = "assemble$name"
            log("依赖任务$elements")
            task.doLast {
                val config = project.extensions.getByName("oss") as OssConfig
                val accessKeyId = config.access_key_id
                val accessKeySecret = config.access_key_secret
                val endpoint = config.endpoint
                val bucket = config.bucket
                val buildOutputs = android.buildOutputs
                buildOutputs.firstOrNull { it.baseName.upFirstDeleteLine == variantName }?.let {
                    log(it.outputFile.absolutePath + "---" + it.baseName)
                    val file = it.outputFile
                    val targetName = if (config.remoteName.isEmpty()) {
                        "App_${name}_${variant.defaultConfig.versionName}_${it.versionCode}.apk"
                    } else {
                        config.remoteName.replace("holder", name)
                    }

                    val remotePath = "${config.remotePath}/$targetName"

                    val client = OSSClient(endpoint, accessKeyId, accessKeySecret)
                    client.putObject(bucket, remotePath, file)
                    val url = "http://$bucket.$endpoint/$remotePath"
                    val remoteConfigPath =
                        "http://$bucket.$endpoint/${config.remotePath}/${android.defaultConfig.applicationId}.json"
                    val last = kotlin.runCatching {
                        val readText = URL(remoteConfigPath).readText()
                        val bean: DevUpdateBean =
                            Gson().fromJson(readText, DevUpdateBean::class.java)
                        bean.hash
                    }.getOrDefault("")
                    log("执行完成,链接为：$url")
                    val cammand = "git log --oneline -n 10"
                    val exec = Runtime.getRuntime().exec(cammand)
                    val readText = exec.inputStream.bufferedReader().readText()
                    val map = readText.split('\n').filter { it.isNotEmpty() }.map {
                        val split = it.split(" ")
                        split[0] to split[1]
                    }.filter { !it.second.startsWith("Merge") }
                    val index = map.map { it.first }.indexOf(last)
                    val updateInfo = map.take(if (index != -1) index else 10).map { it.second }
                        .joinToString("\n")

                    val bean = DevUpdateBean(channel = name.toLowerCase(Locale.ROOT),devVersion,System.currentTimeMillis(),url,android.defaultConfig.versionCode,
                    hash = map.first().first,info = updateInfo)
                    val json = jsonStringOf(bean)

                    client.putObject(
                        bucket,
                        "${config.remotePath}/${android.defaultConfig.applicationId}.json",
                        StringInputStream(json)
                    )

                    log("说明路径为：$remoteConfigPath")
                    client.shutdown()
                    copyUrlToClipboard(url)
                    showUrlQrcode(url = url, name = targetName)
                    kotlin.runCatching {
                        log(URL("https://ali.linwoain.com/appUpdate/devUpdate").readText())
                    }
                } ?: log("未找到debug类型文件")

            }
                .doFirst { log("开始执行此task") }
                .setDependsOn(mutableListOf(elements))
        }


    }

    private fun copyUrlToClipboard(url: String) {
        val runtime = Runtime.getRuntime()
        if (isMac) {
            val file = File.createTempFile("tmp_url", ".sh")
            file.writeText("echo $url | pbcopy")
            log("生成文件路径为：" + file.absolutePath)
            val exec = runtime.exec("chmod +x ${file.absolutePath}")
            if (exec.waitFor() == 0) {
                if (runtime.exec("sh ${file.absolutePath}").waitFor() == 0) {
                    println("路径已拷贝到剪切板")

                }
            }

        } else if (isLinux) {

            val process = runtime.exec("which xclip")
            val path = if (process.waitFor() == 0) {
                process.inputStream.bufferedReader().readText().trim()
            } else {
                System.err.println("复制url到剪切板失败，使用 sudo apt install xclip 安装xclip后重试")
                return
            }

            val file = File.createTempFile("tmp_url", ".sh")
            file.writeText("echo \"$url\" | $path -selection clipboard")
            log("生成文件路径为：" + file.absolutePath)
            val exec = runtime.exec("chmod +x ${file.absolutePath}")

            if (exec.waitFor() == 0) {
                if (runtime.exec("sh ${file.absolutePath}").waitFor() == 0) {
                    println("路径已拷贝到剪切板")
                }
            } else {
                System.err.println("執行命令失敗")
            }


        } else {

            val file = File.createTempFile("tmp_url", ".bat")
            file.writeText("echo $url | clip")
            if (runtime.exec(file.absolutePath).waitFor() == 0) {
                println("路径已拷贝到剪切板")

            }
        }
    }

    private fun showUrlQrcode(
        url: String,
        name: String
    ) {
        val hints = Hashtable<EncodeHintType, Any>()
        hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H
        hints[EncodeHintType.CHARACTER_SET] = "utf-8"
        hints[EncodeHintType.MARGIN] = 1
        val bitMatrix = MultiFormatWriter().encode(
            url,
            BarcodeFormat.QR_CODE,
            300,
            350,
            hints
        )
        val width = bitMatrix.width
        val height = bitMatrix.height
        val image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)

        for (x in 0 until width) {
            for (y in 0 until height) {
                image.setRGB(x, y, if (bitMatrix.get(x, y)) -0x1000000 else -0x1)
            }
        }
        val graphics = image.graphics
        graphics.color = Color.black
        var stringBounds = graphics.fontMetrics.getStringBounds(name, graphics)

        var strWidth = stringBounds.width.toInt()
        if (strWidth < 300) {
            val i = (300 - strWidth) / 2
            graphics.drawString(name, i, 330)
        } else {
            val newName = name.removeSuffix(".apk")
            stringBounds = graphics.fontMetrics.getStringBounds(newName, graphics)
            strWidth = stringBounds.width.toInt()
            log("宽度为${stringBounds.width}")
            if (strWidth < 300) {
                val i = (300 - strWidth) / 2
                graphics.drawString(newName, i, 330)
            } else {
                val lineFirst = name.substring(0..name.lastIndex / 2 + 2)
                val lineSecond = name.substring(name.lastIndex / 2 + 3, name.length)
                stringBounds = graphics.fontMetrics.getStringBounds(lineFirst, graphics)
                strWidth = stringBounds.width.toInt()
                var i = (300 - strWidth) / 2
                graphics.drawString(lineFirst, i, 330)
                stringBounds = graphics.fontMetrics.getStringBounds(lineSecond, graphics)
                strWidth = stringBounds.width.toInt()
                i = (300 - strWidth) / 2
                graphics.drawString(lineSecond, i, 330 + stringBounds.height.toInt() + 2)

            }

        }
        val createTempFile = File(System.getProperty("java.io.tmpdir"), "$name.jpg")
        ImageIO.write(image, "JPG", createTempFile)
        log(createTempFile.absolutePath)
        when {
            isMac -> Runtime.getRuntime()
                .exec("open ${createTempFile.absolutePath}")
            isLinux -> Runtime.getRuntime().exec("xdg-open ${createTempFile.absolutePath}")
            else -> Runtime.getRuntime()
                .exec("explorer ${createTempFile.absolutePath}")
        }

    }

    private fun log(any: Any?) {
        if (DEBUG) {
            println(any)
        }
    }
}